package com.teufandsub.socialproximity.services;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.AsyncTask;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import android.widget.Toast;

import com.teufandsub.socialproximity.R;
import com.teufandsub.socialproximity.controller.Constants;
import com.teufandsub.socialproximity.controller.Controller;
import com.teufandsub.socialproximity.gui.HomeActivity;

/**
 * @author Bastien Mathias <bastienmathias@gmail.com>
 * @author Antoine Teuf <antoine.teuf@gmail.com>
 * @version 1.0
 * @since 2013-05-22
 * 
 * Background service to exectute all background action such as location update and pinging to the cloud.
 */

public class BackgroundService extends Service implements LocationListener{

	private static final int DISTANCE_UPDATE = 50; 		  //Location update every 50 meters.
	private static final int TIME_UPDATE = 1000 * 60 * 3; //Location update every 3 minutes 
	private static final int NOTIFICATION_ID = 8888;
	private final IBinder myBinder = new MyBinder();
	private LocationManager locationManager;
	private String provider;
	private boolean isPinging;
	private Controller controller;
	private boolean isUpdating=false;
	
	private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
	ScheduledFuture<?> pingerHandle;
	private static final int DELAY_PING = 30;
	
	@Override
	public IBinder onBind(Intent intent) {
		return myBinder;
	}
	
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
    	Log.i(Constants.APP_NAME, "Location Service onStart");
    	
    	Intent i = new Intent(BackgroundService.this, HomeActivity.class);
    	PendingIntent pi=PendingIntent.getActivity(this, 0,
                i, 0);
    	
    	Notification not = new NotificationCompat.Builder(this)
    						.setContentTitle(getResources().getString(R.string.app_name))
    						.setContentIntent(pi)
    						.setContentText(getResources().getString(R.string.notification_text))
    						.setAutoCancel(false)
    						.setSmallIcon(R.drawable.ic_launcher).build();
    	
    	startForeground(NOTIFICATION_ID, not);
        return Service.START_STICKY;
    }
	
	@Override
	public void onCreate() {
		super.onCreate();
		
		controller = Controller.getInstance();
		locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
		
		if(!isProviderEnabled()){
			Toast.makeText(this, getResources().getString(R.string.gps_disabled), Toast.LENGTH_LONG).show();
		}
		
		Criteria criteria = new Criteria();
		provider = locationManager.getBestProvider(criteria,false);
		locationManager.requestLocationUpdates(provider, TIME_UPDATE, DISTANCE_UPDATE, this);

		this.startPinger();
	}
	
	@Override
	public void onDestroy() {
		super.onDestroy();
		stopPinger();
		Log.e(Constants.APP_NAME, "SERVICE DESTROY");
	}
	
	@Override
	public void onLocationChanged(Location location) {
		if(!isUpdating){
			new UpdateTask().execute(location);
		}	
	}
	
	@Override
	public void onProviderDisabled(String provider) {
	}
	
	@Override
	public void onProviderEnabled(String provider) {
	}
	
	@Override
	public void onStatusChanged(String provider, int status, Bundle extras) {
		// TODO Auto-generated method stub
		
	}
	
	/**
	 * Method to know if the location provider is enabled.
	 *  
	 * @return boolean return true if the location provider is available, otherwise return false.
	 */
	public boolean isProviderEnabled(){
		return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) 
				&& locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER); }
	
	/**
	 * Method to force an update of the user's position.
	 */
	public void updatePosition(){
		if(!isUpdating){
			new UpdateTask().execute(locationManager.getLastKnownLocation(provider));
		}
	}
	
	/**
	 * Method used to start to ping the server to notify the application is still connected.
	 */
	public void startPinger() {
		isPinging=true;
		final Runnable pinger = new Runnable() {
			public void run() {
				Controller.getInstance().stayActive(BackgroundService.this, true);
			}
		};
		pingerHandle = scheduler.scheduleAtFixedRate(pinger, 0, DELAY_PING, TimeUnit.SECONDS);
		Log.i(Constants.APP_NAME, "ping request sended");
	}
	
	/**
	 * Method used to stop the ping and notify the cloud the user is disconnected.
	 */
	public void stopPinger() {
		isPinging=false;
		new AsyncTask<Void, Void, Void>() {
			@Override
			protected Void doInBackground(Void... params) {
				Controller.getInstance().stayActive(BackgroundService.this, false);
				return null;
			}
		}.execute();
		pingerHandle.cancel(true);
	}
	
	/**
	 * Method to know if the application is currently pinging the cloud.
	 * @return boolean true if application is pinging otherwise return false
	 */
	public boolean isPinging() {
		return isPinging;
	}

	/**
	 * 
	 * Public class to bing the service to an activity.
	 *
	 */
	public class MyBinder extends Binder{
		
		public BackgroundService getInfo(){
			return BackgroundService.this;
		}
	}
	
	/**
	 * 
	 * Private class to update location of the user.
	 *
	 */
	private class UpdateTask extends AsyncTask<Location, Void, Void>{
		
		@Override
		protected void onPreExecute() {
			super.onPreExecute();
			isUpdating=true;
		}
		
		@Override
		protected Void doInBackground(Location... params) {
			controller.updateLocation(BackgroundService.this, params[0].getLatitude(), params[0].getLongitude());
			return null;
		}
		
		@Override
		protected void onPostExecute(Void result) {
			super.onPostExecute(result);
			isUpdating=false;
		}
	}
}
